﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Nyx
{
    /// <summary>
    /// A component used to manage and render a grid.
    /// </summary>
    public class GridComponent
    {
        BasicEffect basicEffect;
        GraphicsDevice graphics;
        int height;
        Matrix projectionMatrix;
        VertexPositionColor[] vertices;
        Matrix viewMatrix;
        int width;

        const float DefaultAlpha = .33f;
        const int DefaultCellSize = 32;

        /// <summary>
        /// Initializes a new instance of the <see cref="GridComponent"/> class.
        /// </summary>
        /// <param name="graphics">The graphics device used to render the grid.</param>
        /// <param name="width">The width of the grid.</param>
        /// <param name="height">The height of the grid.</param>
        public GridComponent(GraphicsDevice graphics, int width, int height)
        {
            if (graphics == null)
                throw new ArgumentNullException("graphics");
            this.width = Math.Max(width, 1);
            this.height = Math.Max(height, 1);
            this.graphics = graphics;
            basicEffect = new BasicEffect(graphics);
            this.Alpha = DefaultAlpha;
            this.CellSize = DefaultCellSize;
            viewMatrix = Matrix.CreateLookAt(new Vector3(0f, 0f, 1f), Vector3.Zero, Vector3.Up);
            this.CreateCellVertices();
        }

        /// <summary>
        /// Gets or sets the alpha.
        /// </summary>
        /// <value>The alpha.</value>
        public float Alpha { get; set; }

        /// <summary>
        /// Gets or sets the size of the cells in the grid (in pixels square).
        /// </summary>
        /// <value>The size of the cells in the grid (in pixels square).</value>
        public int CellSize { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="GridComponent"/> is enabled.
        /// </summary>
        /// <value><c>true</c> if enabled; otherwise, <c>false</c>.</value>
        public bool Enabled { get; set; }

        /// <summary>
        /// Creates the cell vertices.
        /// </summary>
        public void CreateCellVertices()
        {
            int rows = (height / this.CellSize) + 1;
            int cols = (width / this.CellSize) + 1;
            vertices = new VertexPositionColor[rows * cols * 8];
            int i = 0;
            for (int y = 0; y < rows; y++)
            {
                for (int x = 0; x < cols; x++)
                {
                    vertices[i] = new VertexPositionColor(new Vector3((x * this.CellSize), (y * this.CellSize), 0f), Color.White);
                    vertices[i + 1] = new VertexPositionColor(new Vector3((x * this.CellSize) + this.CellSize, (y * this.CellSize), 0f), Color.White);

                    vertices[i + 2] = new VertexPositionColor(new Vector3((x * this.CellSize) + this.CellSize, (y * this.CellSize), 0f), Color.White);
                    vertices[i + 3] = new VertexPositionColor(new Vector3((x * this.CellSize) + this.CellSize, (y * this.CellSize) + this.CellSize, 0f), Color.White);

                    vertices[i + 4] = new VertexPositionColor(new Vector3((x * this.CellSize) + this.CellSize, (y * this.CellSize) + this.CellSize, 0f), Color.White);
                    vertices[i + 5] = new VertexPositionColor(new Vector3((x * this.CellSize), (y * this.CellSize) + this.CellSize, 0f), Color.White);

                    vertices[i + 6] = new VertexPositionColor(new Vector3((x * this.CellSize), (y * this.CellSize) + this.CellSize, 0f), Color.White);
                    vertices[i + 7] = new VertexPositionColor(new Vector3((x * this.CellSize), (y * this.CellSize), 0f), Color.White);

                    i += 8;
                }
            }
        }

        /// <summary>
        /// Draws the grid.
        /// </summary>
        /// <param name="transform">The transformation matrix.</param>
        public void Draw(Matrix transform)
        {
            basicEffect.World = transform;
            basicEffect.View = viewMatrix;
            projectionMatrix = Matrix.CreateOrthographicOffCenter(0f,
                graphics.Viewport.Width,
                graphics.Viewport.Height,
                0f,
                0f, 1f);
            basicEffect.Projection = projectionMatrix;
            basicEffect.Alpha = this.Alpha;
            graphics.BlendState = BlendState.AlphaBlend;
            foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
            {
                pass.Apply();
                graphics.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.LineList, vertices, 0, vertices.Length / 2);
            }
        }
    }
}
